#include <iostream>
#include <vector>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstdlib>
#include <ctime>
#include <cassert>
#include "Task.hpp"
#define PROCESS_NUM 5 // 创建进程个数
using namespace std;
int waitCommand(int waitFd, bool &quit)
{
    // uint32_t是4个字节
    uint32_t command = 0;
    // read函数第一个参数是要去哪个文件描述符读取,sizeof(command)表示只读取4个字节
    ssize_t s = read(waitFd, &command, sizeof(command));
    if (s == 0)
    {
        quit = true;
        return -1;
    }
    assert(s == sizeof(uint32_t));
    return command; // 读取成功command命令并返回
}
void sendAndWakeup(pid_t who, int fd, uint32_t command)
{
    write(fd, &command, sizeof(command));
    cout << "call process" << who << "execute " << desc[command] << "through" << fd << endl;
}
int main()
{
    load();
    // pair是一个模板类，这里当成对象类型
    vector<pair<pid_t, int>> slots;
    // 先创建多个进程
    for (int i = 0; i < PROCESS_NUM; i++)
    {
        // 创建管道
        int pipefd[2] = {0};
        int n = pipe(pipefd); // pipe返回值，管道创建成功返回0，失败返回-1
        assert(n == 0);
        (void)n;
        pid_t id = fork();
        assert(id != -1);
        // 子进程读取
        if (id == 0)
        {
            // 子进程，关闭写端
            close(pipefd[1]);
            while (true)
            {
                // 等命令
                bool quit = false;
                int command = waitCommand(pipefd[0], quit);
                if (quit)
                    break;
                // 执行对应的命令
                if (command >= 0 && HandlerSize()) // 规定命令的范围
                {
                    callbacks[command]();
                }
                else
                {
                    cout << "非法command:" << command << endl;
                }
            }
            exit(1); // 子进程退出
        }
        // 父进程,关闭读端
        close(pipefd[0]);
        // 参数放入id是因为在父进程中,该id就是子进程的id，
        // 写端,父进程把子进程以及管道放入一个键值对数组，形成一张表
        slots.push_back(pair<pid_t, int>(id, pipefd[1])); // pair<pid_t,int>
    }
    // 父进程派发任务任务
    srand((unsigned long)time(nullptr) ^ getpid() ^ 233124214L); // 用time作为随机数种子，括号的参数都是为了让数据源更随机性
    while (true)
    {
        int select;
        int command;
        cout << "#########################################" << endl;
        cout << "#  1. show functions     2. send command#" << endl;
        cout << "#########################################" << endl;
        cout << "please Select> ";
        cin >> select;
        if (select == 1)
            shwoHandler();
        else if (select == 2) // 用户正常发送命令
        {
            cout << "Enter Your Command> " << endl;
            // 选择任务
            cin >> command;
            // 选择进程
            int choice = rand() % slots.size(); // 随机数%进程数，值一直在0~4范围内
            // 把任务给指定进程,参数意思为：指定进程   对应的文件描述符
            sendAndWakeup(slots[choice].first, slots[choice].second, command); // 发送命令并让子进程苏醒干活
        }
    }
    // 关闭fd,结束所有进程
    for (const auto &slot : slots)
    {
        close(slot.second);
    }
    // 回收所有子进程信息
    for (const auto &slot : slots)
    {
        waitpid(slot.first, nullptr, 0);
    }
    return 0;
}